/*
 * Copyright (c) 2021. Dylan Cai
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

@file:Suppress("unused")

package com.price.common.extend

import android.app.RecoverableSecurityException
import android.content.ContentUris
import android.database.Cursor
import android.graphics.Bitmap
import android.net.Uri
import android.os.Build
import android.os.Build.VERSION
import android.os.CancellationSignal
import android.os.Environment
import android.os.ParcelFileDescriptor
import android.provider.BaseColumns
import android.provider.DocumentsContract
import android.provider.MediaStore
import android.provider.OpenableColumns
import android.text.TextUtils
import android.util.Log
import android.util.Size
import android.webkit.MimeTypeMap
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.IntentSenderRequest
import androidx.annotation.RequiresApi
import androidx.core.content.FileProvider
import java.io.BufferedInputStream
import java.io.BufferedOutputStream
import java.io.File
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.util.Locale


lateinit var fileProviderAuthority: String

inline val EXTERNAL_MEDIA_IMAGES_URI: Uri
    get() = MediaStore.Images.Media.EXTERNAL_CONTENT_URI

inline val EXTERNAL_MEDIA_VIDEO_URI: Uri
    get() = MediaStore.Video.Media.EXTERNAL_CONTENT_URI

inline val EXTERNAL_MEDIA_AUDIO_URI: Uri
    get() = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI

@get:RequiresApi(Build.VERSION_CODES.Q)
inline val EXTERNAL_MEDIA_DOWNLOADS_URI: Uri
    get() = MediaStore.Downloads.EXTERNAL_CONTENT_URI

fun File.toUri(authority: String = fileProviderAuthority): Uri =
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        FileProvider.getUriForFile(application, authority, this)
    } else {
        Uri.fromFile(this)
    }

fun Uri.update(vararg pairs: Pair<String, Any?>): Boolean =
    contentResolver.update(
        this, *pairs, where = "${BaseColumns._ID} = ?",
        selectionArgs = arrayOf(ContentUris.parseId(this).toString())
    ) > 0

@ExperimentalApi
fun Uri.delete(launcher: ActivityResultLauncher<IntentSenderRequest>): Boolean =
    @Suppress("DEPRECATION")
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
        val projection = arrayOf(MediaStore.MediaColumns.DATA)
        contentResolver.queryFirst(this, projection) { cursor ->
            File(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA))).delete()
        } ?: false
    } else {
        try {
            val id: Long = ContentUris.parseId(this)
            contentResolver.delete(this, "${BaseColumns._ID} = ?", arrayOf(id.toString())) > 0
        } catch (securityException: SecurityException) {
            val recoverableSecurityException = securityException as? RecoverableSecurityException
                ?: throw RuntimeException(securityException.message, securityException)
            val intentSender =
                recoverableSecurityException.userAction.actionIntent.intentSender
            launcher.launch(IntentSenderRequest.Builder(intentSender).build())
            false
        }
    }

inline fun <R> Uri.openFileDescriptor(
    mode: String = "r",
    crossinline block: (ParcelFileDescriptor) -> R
): R? =
    contentResolver.openFileDescriptor(this, mode)?.use(block)

inline fun <R> Uri.openInputStream(crossinline block: (InputStream) -> R): R? =
    contentResolver.openInputStream(this)?.use(block)

inline fun <R> Uri.openOutputStream(crossinline block: (OutputStream) -> R): R? =
    contentResolver.openOutputStream(this)?.use(block)

@RequiresApi(Build.VERSION_CODES.Q)
fun Uri.loadThumbnail(width: Int, height: Int, signal: CancellationSignal? = null): Bitmap =
    contentResolver.loadThumbnail(this, Size(width, height), signal)

inline val Uri.fileExtension: String?
    get() = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType)

inline val Uri.mimeType: String?
    get() = contentResolver.getType(this)

val Uri.size: Long
    get() = try {
        contentResolver.openFileDescriptor(this, "r")?.statSize
    } catch (e: FileNotFoundException) {
        contentResolver.queryFirst(this, arrayOf(OpenableColumns.SIZE)) { cursor ->
            cursor.getLong(cursor.getColumnIndexOrThrow(OpenableColumns.SIZE))
        }
    } ?: 0


fun Uri.isExternalStorageDocument():Boolean {
    return "com.android.externalstorage.documents" == this.authority;
}

fun Uri.isDownloadsDocument():Boolean {
    return "com.android.providers.downloads.documents" == this.authority
}

fun Uri.isMediaDocument():Boolean {
    return "com.android.providers.media.documents" == this.authority
}

fun Uri.isGooglePhotosUri():Boolean {
    return "com.google.android.apps.photos.content" == this.authority
}

fun Uri.getDataColumn(selection:String?, selectionArgs:Array<String>?):String? {
    var cursor:Cursor? = null
    val column = "_data"
    val projection = arrayOf(column)

    var path:String?=null
    try {
        cursor = context.contentResolver.query(this, projection, selection, selectionArgs, null)
        if (cursor != null && cursor.moveToFirst()) {
            val columnIndex = cursor.getColumnIndexOrThrow(column)
            path = cursor.getString(columnIndex)
        }
    } catch ( var12:IllegalArgumentException) {
        Log.i("FileUtils", String.format(Locale.getDefault(), "getDataColumn: _data - [%s]", var12.message))
    } finally {
        cursor?.close()
    }

    return path
}

fun Uri.toPath(): String? {
    val isKitKat = VERSION.SDK_INT >= 19
    if (isKitKat && DocumentsContract.isDocumentUri(context, this)) {
        val id: String
        val split: Array<String>
        val type: String
        if (isExternalStorageDocument()) {
            id = DocumentsContract.getDocumentId(this)
            split = id.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
            type = split[0]
            if ("primary".equals(type, ignoreCase = true)) {
                return Environment.getExternalStorageDirectory().toString() + "/" + split[1]
            }
        } else if (isDownloadsDocument()) {
            id = DocumentsContract.getDocumentId(this)
            if (!TextUtils.isEmpty(id)) {
                return try {
                    val contentUri = ContentUris.withAppendedId(
                        Uri.parse("content://downloads/public_downloads"),
                        java.lang.Long.valueOf(id)
                    )
                    contentUri.getDataColumn(null, null) ?:getFilePathFromURI()
                } catch (var9: NumberFormatException) {
                    Log.i("FileUtils", var9.message!!)
                    null
                }
            }
        } else if (isMediaDocument()) {
            id = DocumentsContract.getDocumentId(this)
            split = id.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
            type = split[0]

            val contentUri: Uri? = if ("image" == type) {
                 MediaStore.Images.Media.EXTERNAL_CONTENT_URI
            } else if ("video" == type) {
                 MediaStore.Video.Media.EXTERNAL_CONTENT_URI
            } else if ("audio" == type) {
                MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
            }else{
                null
            }
            val selection = "_id=?"
            val selectionArgs = arrayOf(split[1])
            return contentUri?.getDataColumn(selection, selectionArgs) ?: getFilePathFromURI()
        }
    } else {
        if ("content".equals(scheme, ignoreCase = true)) {
            return if (isGooglePhotosUri()) {
                lastPathSegment
            } else getDataColumn(null, null) ?: getFilePathFromURI()
        }
        if ("file".equals(scheme, ignoreCase = true)) {
            return path
        }
    }
    return null
}

private fun Uri.getFilePathFromURI(): String? {
    val rootDataDir: File = context.filesDir
    val fileName = getFileName()
    if (!TextUtils.isEmpty(fileName)) {
        val copyFile = File(rootDataDir , fileName)
        copyFile(copyFile)
        return copyFile.absolutePath
    }
    return null
}

private fun Uri.getFileName(): String? {
    var fileName: String? = null
    val path = path!!
    val cut = path.lastIndexOf('/')
    if (cut != -1) {
        fileName = path.substring(cut + 1)
    }
    return fileName
}

private fun Uri.copyFile(dstFile: File) {
    try {
        val inputStream: InputStream = context.contentResolver.openInputStream(this)
            ?: return
        val outputStream: OutputStream = FileOutputStream(dstFile)
        copyStream(inputStream, outputStream)
        inputStream.close()
        outputStream.close()
    } catch (e: java.lang.Exception) {
        e.printStackTrace()
    }
}

@Throws(java.lang.Exception::class)
private fun copyStream(input: InputStream, output: OutputStream): Int {
    val BUFFER_SIZE = 1024 * 2
    val buffer = ByteArray(BUFFER_SIZE)
    val `in` = BufferedInputStream(input, BUFFER_SIZE)
    val out = BufferedOutputStream(output, BUFFER_SIZE)
    var count = 0
    var n = 0
    try {
        while (`in`.read(buffer, 0, BUFFER_SIZE).also { n = it } != -1) {
            out.write(buffer, 0, n)
            count += n
        }
        out.flush()
    } finally {
        try {
            out.close()
        } catch (e: IOException) {
            e.printStackTrace()
        }
        try {
            `in`.close()
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }
    return count
}
